import Vue from 'vue'
function render(createElement, render_v_node, context, instance = undefined) {
    // render_v_node can is func or class<vNode>
    if (render_v_node != undefined & render_v_node != null)
        return this.createNode(createElement, render_v_node, context, instance)
    else
        throw new TypeError(`[render] new render 'render_v_node' is undefined or null`)
}

render.prototype.createNode = function (createElement, render_v_node, context, instance = undefined) {
    //this.Debug([`[render] ${render_v_node.el}, attrs:`, render_v_node.attrs])
    if ('on' in render_v_node.attrs) {
        for (let o in render_v_node.attrs.on) {
            if (typeof (render_v_node.attrs.on[o]) === "string") {
                //需要解决方法传参问题。
                render_v_node.attrs.on[o] = Reflect.get(instance, render_v_node.attrs.on[o])
            }
        }
    }
    let a=Reflect.get(context,'vtext')
    //console.log('bbbbbbbbbbbbbeeeeeeeeeeeeeeeeee',a)
    if('dataSource' in render_v_node){
        if(typeof(render_v_node.dataSource)==="string"){
            if(render_v_node.dataSource.indexOf("self.")>-1){
                render_v_node.dataSource= Reflect.get(context,render_v_node.dataSource.replace("self.",""))
            }
        }
    }
    return createElement(render_v_node.el, this.injectInstance(render_v_node.attrs, context), (render_v_node.text != undefined) ? (() => {
        if (render_v_node.dataSource != null) {
            //if(render_v_node.text=='self.ttext')
            return this.convertSelfValue(render_v_node.text, render_v_node.dataSource, 'data.')
        } else {
            return this.convertSelfValue(render_v_node.text, context, 'self.')
        }
    })() : (render_v_node.childs instanceof Array) ? ((render_v_node) => {
        let v = []
        render_v_node.forEach(o => {
            switch (o.type.toLowerCase()) {
                case "element": {
                    if (o.dataSource != undefined) {
                        if (Array.isArray(o.dataSource)) {
                            v.push(o.dataSource.map(function (data) {
                                return new render(createElement, o, data, instance)
                            }))
                        } else {
                            v.push(this.createNode(createElement, this.convertSelfValue(o, o.dataSource, 'data.'), context, instance))
                        }
                    }
                    else {
                        v.push(this.createNode(createElement, o, context, instance))
                    }
                    break
                }
                case "slots": {
                    this.createSlot(createElement, o, context, instance)
                    //v.push(this.createNode(createElement, o, context))
                    break;
                }
                case "scopedslots": {
                    // to set scopedSlots.
                    this.createScopedSlots(createElement, o, context, instance = undefined)
                    //v.push(this.createNode(createElement, o, context))
                    break
                }
                default: {
                    throw new ReferenceError(`[ERROR] render.createNode createElement 'render_v_node'(${o.type}) is error.`);
                }
            }
        });
        return v
    })(render_v_node.childs) : this.convertSelfValue(render_v_node.childs, context))
}

render.prototype.createSlot = function (createElement, render_v_node, context, instance = undefined) {
    let _v_node = (render_v_node.childs != null && render_v_node.childs != undefined) ? (render_v_node.childs.length == 1) ? render_v_node.childs[0] : undefined : undefined
    if (_v_node != undefined) {
        context.$slots[render_v_node.el] = Vue.component(render_v_node.el,
            {
                name: render_v_node.el,
                render: function (createElement) {
                    return this.createNode(createElement, _v_node, context, instance)
                }
            }
        )
    }
    else {
        throw new TypeError(`[ERROR] create slot error. slot(${render_v_node.el}) vNode has error, only one root. given slot vNode: ${render_v_node.childs}`)
    }
}

render.prototype.createScopedSlots = function (createElement, render_v_node, context, instance = undefined) {
    // has bug...... createScopedSlots: scopedSlotsName: scopedSlotsName(scoped args map)
    let _v_node = (render_v_node.childs != null && render_v_node.childs != undefined) ? (render_v_node.childs.length == 1) ? render_v_node.childs[0] : undefined : undefined
    context.$scopedSlots[render_v_node.el](this.injectInstance(render_v_node.attrs, context))
}
render.prototype.convertSelfValue = function (target, context, findStr = 'self.') {
    if (typeof (target) != 'string') return target
    if (target.indexOf(findStr) != -1) {
        if (target.replace(findStr, '') in context) {
            target = Reflect.get(context, target.replace(findStr, ''))
        }
        else {
            throw new TypeError(`[render]: context can not found key(${target.replace(findStr, '')})`)
        }
    }
    return target
}

render.prototype.injectInstance = function (target, context, findStr = "self.") {
    let result = Object.assign({}, target)
    for (let o in result) {
        switch (typeof (result[o])) {
            case "object": {
                result[o] = this.injectInstance(result[o], context)
                break
            }
            case "string": {
                if (result[o].indexOf(findStr) != -1) {
                    //alert(o)
                    if (result[o].replace(findStr, '') in context) {
                        result[o] = Reflect.get(context, result[o].replace(findStr, ''))
                    }
                    else {
                        throw new TypeError(`[render]: context can not found key(${result[o].replace(findStr, '')})`)
                    }
                }
                break
            }
            default: {
                result[o] = result[o]
            }
        }
    }
    return result
}
render.prototype.ISPROCUCTION = function () {
    return process.env.NODE_ENV === 'production' ? true : false
}

render.prototype.Debug = function (info = []) {
    if (!this.ISPROCUCTION()) {
        info.forEach(function (o) {
            console.log("%O", o)
        });
    }
}
export default render